package com.example.demo.controller;

import com.example.demo.auth.Permission;
import com.example.demo.dto.ErrorCode;
import com.example.demo.dto.ResponseValue;
import com.example.demo.dto.common.*;
import com.example.demo.dto.input.CommonInput;
import com.example.demo.entity.Consultant;
import com.example.demo.entity.Supervisor;
import com.example.demo.entity.User;
import com.example.demo.entity.relation.ConsultRecord;
import com.example.demo.enums.Role;
import com.example.demo.exception.EntityAlreadyExistException;
import com.example.demo.exception.EntityNotExistException;
import com.example.demo.exception.IMIDHadCreatedException;
import com.example.demo.exception.IMIDNotExistException;
import com.example.demo.redis.RedisKey;
import com.example.demo.service.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping(value = "/user")
public class UserController {
    private final UserService userService;
    private final RedisTemplate<String, Object> redisTemplate;
    private final AliService aliService;
    private final ConsultantService consultantService;
    private final IMService imService;
    private final SupervisorService supervisorService;

    public UserController(UserService userService, RedisTemplate<String, Object> redisTemplate, AliService aliService,
                          ConsultantService consultantService, IMService imService, SupervisorService supervisorService){
        this.userService = userService;
        this.redisTemplate = redisTemplate;
        this.aliService = aliService;
        this.consultantService = consultantService;
        this.imService = imService;
        this.supervisorService = supervisorService;
    }

    @RequestMapping(method = RequestMethod.POST, value = "/code")
    public ResponseValue<?> getCode(@RequestBody CommonInput input) throws Exception {
        Optional<CommonInput> optionalInput = Optional.of(input);
        String number = optionalInput.map(CommonInput::getUser).map(UserModel::getPhoneNum).orElse("");
        if(number.length() != 11){
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }
        String code = aliService.sendCode(number);
        if(code == null) return ResponseValue.fail(ErrorCode.GET_SMS_CODE_ERROR);
        String redisKey = RedisKey.USER_CODE.replace("{number}", number);
        redisTemplate.opsForValue().set(redisKey, code, 5, TimeUnit.MINUTES);
        UserModel model = new UserModel();
        model.setCertificationCode(code);
        CommonInput output = new CommonInput();
        output.setUser(model);
        return ResponseValue.success(output);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/register")
    public ResponseValue<?> register(@RequestBody CommonInput input) throws EntityAlreadyExistException, IOException, IMIDHadCreatedException, IMIDNotExistException {
        Optional<CommonInput> optionalInput = Optional.of(input);
        UserModel model = optionalInput.map(CommonInput::getUser).orElse(null);
        String number = optionalInput.map(CommonInput::getUser).map(UserModel::getPhoneNum).orElse("");
        String inputCode = optionalInput.map(CommonInput::getUser).map(UserModel::getCertificationCode).orElse("");
        if(inputCode.length()!=6 || number.length()!=11 || model==null){
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }
        String redisKey = RedisKey.USER_CODE.replace("{number}", number);
        String code = (String)redisTemplate.opsForValue().get(redisKey);
        if(code != null && code.equals(inputCode)){
            User user = new User();
            user.setName(model.getName());
            user.setId(model.getUserId());
            user.setPhoneNumber(model.getPhoneNum());
            user.setUrgentName(model.getUrgentContact());
            user.setUrgentNumber(model.getUrgentContactNum());
            String num = userService.addUser(user);
            model = new UserModel();
            model.setPhoneNum(num);
            if(imService.isOriginIdUsed(number)){
                model.setIMUserID(imService.queryIMId(number));
            }else{
                model.setIMUserID(imService.generateIMId(number));
            }
            model.setIMUserSig(imService.getIMToken(number));
            CommonInput output = new CommonInput();
            output.setUser(model);
            return ResponseValue.success(output);
        }
        return ResponseValue.fail(ErrorCode.WRONG_SMS_CODE);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/userInfo")
    public ResponseValue<?> getUserInfo(@RequestBody CommonInput input) throws EntityNotExistException, IOException, IMIDNotExistException, IMIDHadCreatedException {
        Optional<CommonInput> optionalInput = Optional.of(input);
        String num = optionalInput.map(CommonInput::getUser).map(UserModel::getPhoneNum).orElse("");
        if(num.length()==11){
            User user = userService.getUserByNumber(num);
            UserModel model = new UserModel();
            model.setName(user.getName());
            model.setPhoneNum(user.getPhoneNumber());
            model.setUserId(user.getId());
            model.setUrgentContactNum(user.getUrgentNumber());
            model.setUrgentContact(user.getUrgentName());
            if(imService.isOriginIdUsed(num)){
                model.setIMUserID(imService.queryIMId(num));
            }else{
                model.setIMUserID(imService.generateIMId(num));
            }
            model.setIMUserSig(imService.getIMToken(num));
            CommonInput output = new CommonInput();
            output.setUser(model);
            return ResponseValue.success(output);
        }
        return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/records")
    public ResponseValue<?> getConsultRecords(@RequestBody CommonInput input) throws IOException, IMIDHadCreatedException, IMIDNotExistException {
        Optional<CommonInput> optionalInput = Optional.of(input);
        String number = optionalInput.map(CommonInput::getUser).map(UserModel::getPhoneNum).orElse("");
        if(number.length()==11){
            List<ConsultRecord> list=userService.getConsultRecords(number);
            List<ConsultHistoryModel> modelList = new ArrayList<>();
            for(ConsultRecord record: list){
                ConsultHistoryModel model = new ConsultHistoryModel();
                UserModel userModel = new UserModel();
                ConsultantModel consultantModel = new ConsultantModel();
                userModel.setPhoneNum(record.getUserId());
                userModel.setName(record.getUserName());
                consultantModel.setId(record.getConsultantId());
                consultantModel.setName(record.getConsultantName());
                model.setId(record.getId());
                model.setTime(record.getTime());
                model.setConsultType(record.getConsultType());
                model.setDuration(record.getDuration());
                model.setEvaluateScore(record.getEvaluateScore());
                model.setEvaluateText(record.getEvaluateText());
                if(imService.isOriginIdUsed(number)){
                    userModel.setIMUserID(imService.queryIMId(number));
                }else{
                    userModel.setIMUserID(imService.generateIMId(number));
                }
                userModel.setIMUserSig(imService.getIMToken(number));
                String id = record.getConsultantId();
                if(imService.isOriginIdUsed(id)){
                    consultantModel.setImId(imService.queryIMId(id));
                }else{
                    consultantModel.setImId(imService.generateIMId(id));
                }
                consultantModel.setImSig(imService.getIMToken(id));
                model.setUser(userModel);
                model.setConsultant(consultantModel);
                modelList.add(model);
            }
            CommonInput output = new CommonInput();
            output.setConsultant_history_list(modelList);
            return ResponseValue.success(output);
        }
        return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/consultants")
    public ResponseValue<?> getOnlineConsultants(@RequestBody CommonInput input) throws IMIDHadCreatedException, IOException, IMIDNotExistException, EntityNotExistException {
        Set<Object> set = redisTemplate.opsForSet().members(RedisKey.CONSULTANT_ONLINE_SET);
        assert set != null;
        Iterator<Object> iterator = set.iterator();
        List<String> ids = new ArrayList<>();
        for(Object obj:set){
            ids.add((String)obj);
        }
        List<Consultant> consultants = consultantService.getConsultants(ids);
        List<ConsultantModel> modelList = new ArrayList<>();
        for(Consultant consultant:consultants){
            ConsultantModel model = new ConsultantModel();
            String id = consultant.getId();
            if("true".equals(consultant.getIsSupervisor())){
                Supervisor supervisor = supervisorService.getSupervisorByConsultantId(id);
                model.setSuperviseCount(supervisor.getSuperviseCount());
                model.setSuperviseTotalMinutes(supervisor.getSuperviseTime());
            }
            model.setName(consultant.getName());
            model.setAverageEvaluation(consultant.getEvaluation());
            model.setId(consultant.getId());
            model.setPhoneNum(consultant.getPhoneNumber());
            model.setIsSupervise(consultant.getIsSupervisor());
            model.setAge(String.valueOf(consultant.getAge()));
            model.setCompany(consultant.getCompany());
            model.setEmail(consultant.getEmail());
            model.setGender(consultant.getGender());
            if(imService.isOriginIdUsed(id)){
                model.setImId(imService.queryIMId(id));
            }else{
                model.setImId(imService.generateIMId(id));
            }
            model.setImSig(imService.getIMToken(id));
            modelList.add(model);
        }
        CommonInput output = new CommonInput();
        output.setConsultant_list(modelList);
        return ResponseValue.success(output);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/evaluate")
    public ResponseValue<?> evaluateConsultant(@RequestBody CommonInput input) throws EntityNotExistException {
        Optional<CommonInput> optional = Optional.of(input);
        ConsultHistoryModel model = optional.map(CommonInput::getConsult_history).orElse(null);
        if(model==null){
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }
        userService.evaluateConsult(model);
        return ResponseValue.success(null);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/login")
    public ResponseValue<?> login(@RequestBody CommonInput input) throws EntityNotExistException, IOException, IMIDHadCreatedException, IMIDNotExistException {
        Optional<CommonInput> optionalInput = Optional.of(input);
        String number = optionalInput.map(CommonInput::getUser).map(UserModel::getPhoneNum).orElse("");
        String inputCode = optionalInput.map(CommonInput::getUser).map(UserModel::getCertificationCode).orElse("");
        if(number.length()!=11){
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }
        Map<String, Object> m = userService.login(number, inputCode);
        if(m!=null){
            UserModel model = new UserModel();
            model.setPhoneNum((String)m.get("number"));
            if(imService.isOriginIdUsed(number)){
                model.setIMUserID(imService.queryIMId(number));
            }else{
                model.setIMUserID(imService.generateIMId(number));
            }
            model.setIMUserSig(imService.getIMToken(number));
            CommonInput output = new CommonInput();
            output.setUser(model);
            return ResponseValue.success(output);
        }
        return ResponseValue.fail(ErrorCode.WRONG_SMS_CODE);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/admin/login")
    public ResponseValue<?> loginForAdmin(@RequestBody CommonInput input){ return null;}

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/ban")
    public ResponseValue<?> banUser(@RequestBody CommonInput input) throws EntityNotExistException {
        Optional<CommonInput> optional = Optional.of(input);
        UserModel model = optional.map(CommonInput::getUser).orElse(null);
        if(model.getPhoneNum()==null || model.getPhoneNum().length()!=11){
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }
        userService.banUser(model.getPhoneNum());
        return ResponseValue.success(null);
    }

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/delete")
    public ResponseValue<?> deleteUser(@RequestBody CommonInput input){
        Optional<CommonInput> optional = Optional.of(input);
        UserModel model = optional.map(CommonInput::getUser).orElse(null);
        if(model==null || model.getPhoneNum()==null || model.getPhoneNum().length()!=11){
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }
        userService.deleteUser(model.getPhoneNum());
        return ResponseValue.success(null);
    }

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/add")
    public ResponseValue<?> addUserByAdmin(@RequestBody CommonInput input) throws EntityAlreadyExistException, IOException, IMIDHadCreatedException, IMIDNotExistException {
        Optional<CommonInput> optional = Optional.of(input);
        UserModel model = optional.map(CommonInput::getUser).orElse(null);
        if(model==null){
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }
        User user = new User();
        user.setName(model.getName());
        user.setId(model.getUserId());
        user.setPhoneNumber(model.getPhoneNum());
        user.setUrgentName(model.getUrgentContact());
        user.setUrgentNumber(model.getUrgentContactNum());
        String number = userService.addUser(user);
        model = new UserModel();
        model.setPhoneNum(number);
        if(imService.isOriginIdUsed(number)){
            model.setIMUserID(imService.queryIMId(number));
        }else{
            model.setIMUserID(imService.generateIMId(number));
        }
        model.setIMUserSig(imService.getIMToken(number));
        CommonInput output = new CommonInput();
        output.setUser(model);
        return ResponseValue.success(output);
    }

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/all")
    public ResponseValue<?> getAllUserInfo(@RequestBody CommonInput input) throws IOException, IMIDHadCreatedException, IMIDNotExistException {
        List<User> users = userService.getAllUsers();
        List<UserModel> models = new ArrayList<>();
        for(User user: users){
            String number = user.getPhoneNumber();
            UserModel model = new UserModel();
            model.setName(user.getName());
            model.setPhoneNum(user.getPhoneNumber());
            model.setUserId(user.getId());
            model.setUrgentContact(user.getUrgentName());
            model.setUrgentContactNum(user.getUrgentNumber());
            if(imService.isOriginIdUsed(number)){
                model.setIMUserID(imService.queryIMId(number));
            }else{
                model.setIMUserID(imService.generateIMId(number));
            }
            model.setIMUserSig(imService.getIMToken(number));
            models.add(model);
        }
        CommonInput output = new CommonInput();
        output.setUser_list(models);
        return ResponseValue.success(output);
    }

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/update")
    public ResponseValue<?> updateUserInfoByAdmin(@RequestBody CommonInput input) throws EntityNotExistException,
            IllegalAccessException {
        Optional<CommonInput> optional = Optional.of(input);
        UserModel model = optional.map(CommonInput::getUser).orElse(null);
        if(model==null) return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        User user = new User();
        user.setName(model.getName());
        user.setId(model.getUserId());
        user.setPhoneNumber(model.getPhoneNum());
        user.setUrgentName(model.getUrgentContact());
        user.setUrgentNumber(model.getUrgentContactNum());
        userService.updateUser(user.getPhoneNumber(), user);
        return ResponseValue.success(null);
    }

    //@Permission(roles = {Role.ADMIN})
    @RequestMapping(method = RequestMethod.POST, value = "/admin/info")
    public ResponseValue<?> getUserInfoByAdmin(@RequestBody CommonInput input) throws EntityNotExistException, IOException, IMIDHadCreatedException, IMIDNotExistException {
        Optional<CommonInput> optional = Optional.of(input);
        String number = optional.map(CommonInput::getUser).map(UserModel::getPhoneNum).orElse("");
        if("".equals(number)){
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }
        User user = userService.getUserByNumber(number);
        UserModel model = new UserModel();
        model.setName(user.getName());
        model.setPhoneNum(user.getPhoneNumber());
        model.setUserId(user.getId());
        model.setUrgentContact(user.getUrgentName());
        model.setUrgentContactNum(user.getUrgentNumber());
        if(imService.isOriginIdUsed(number)){
            model.setIMUserID(imService.queryIMId(number));
        }else{
            model.setIMUserID(imService.generateIMId(number));
        }
        model.setIMUserSig(imService.getIMToken(number));
        model.setPhoneNum(number);
        CommonInput output = new CommonInput();
        output.setUser(model);
        return ResponseValue.success(output);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/im")
    public ResponseValue<?> getIMIdAndSig(@RequestBody CommonInput input) throws EntityNotExistException, IOException, IMIDHadCreatedException, IMIDNotExistException {
        Optional<CommonInput> optional = Optional.of(input);
        String number = optional.map(CommonInput::getUser).map(UserModel::getPhoneNum).orElse("");
        if(number.length()==11){
            User user = userService.getUserByNumber(number);
            UserModel model = new UserModel();
            if(imService.isOriginIdUsed(number)){
                model.setIMUserID(imService.queryIMId(number));
            }else{
                model.setIMUserID(imService.generateIMId(number));
            }
            model.setIMUserSig(imService.getIMToken(number));
            model.setPhoneNum(number);
            CommonInput output = new CommonInput();
            output.setUser(model);
            return ResponseValue.success(output);
        }
        return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
    }

    @RequestMapping(method = RequestMethod.POST, value = "/history/add")
    public ResponseValue<?> addConsultHistory(@RequestBody CommonInput input) throws EntityNotExistException {
        Optional<CommonInput> optional = Optional.of(input);
        ConsultHistoryModel model = optional.map(CommonInput::getConsult_history).orElse(null);
        if(model == null){
            return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
        }
        model = userService.addConsultHistory(model);
        if(model!=null) return ResponseValue.success(input);
        return ResponseValue.fail(ErrorCode.REQUEST_PARAM_ERROR);
    }
}
